package com.xiaodu.springboot.controller.advice;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.xiaodu.springboot.entity.ResultBean;
import com.xiaodu.springboot.ex.BusinessException;
import org.hibernate.validator.internal.engine.path.PathImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.HandlerMethod;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolationException;
import java.io.IOException;
import java.util.*;

/**
 * @author: dutianjie
 * @date: 2021/3/31
 * @version: v1.0.0
 * @description: 描述
 **/

@RestControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    /*默认异常处理*/
    @ExceptionHandler(value = {Exception.class})
    public ResultBean<Void> defExceptionHandler(HttpServletRequest request, HttpServletResponse response, Exception e, HandlerMethod method) throws Exception {
        String message = e.getMessage();
        log.error("error -- message = " + message, e);
        return ResultBean.error(HttpStatus.INTERNAL_SERVER_ERROR.value(), message);
    }

    /*valid 参数校验异常处理*/
    //MethodArgumentNotValidException  ConstraintViolationException
    @ExceptionHandler(value = {ConstraintViolationException.class, MethodArgumentNotValidException.class})
    public ResponseEntity<ResultBean<Void>> constraintValidatedExceptionHandler(HttpServletRequest request, Exception e, HandlerMethod method) {
        ResponseEntity.BodyBuilder responseEntityBuilder = ResponseEntity.badRequest();
        ResultBean<Void> resultBean = ResultBean.result(HttpStatus.BAD_REQUEST);
        StringJoiner stringJoiner = new StringJoiner(";", "异常信息{ ", " }");
        if (e instanceof ConstraintViolationException) {
            ConstraintViolationException constraintViolationException = (ConstraintViolationException) e;
            Optional.ofNullable(constraintViolationException.getConstraintViolations()).orElseGet(HashSet::new)
                    .forEach(v -> {
                        String message = v.getMessage();
                        PathImpl propertyPath = (PathImpl) v.getPropertyPath();
                        String s = propertyPath.getLeafNode().asString();
                        stringJoiner.add(s + ": " + message);
                    });
        }
        if (e instanceof MethodArgumentNotValidException) {
            MethodArgumentNotValidException methodArgumentNotValidException = (MethodArgumentNotValidException) e;
            List<FieldError> fieldErrors = methodArgumentNotValidException.getBindingResult().getFieldErrors();
            Optional.ofNullable(fieldErrors).orElseGet(ArrayList::new).forEach(v -> {
                String field = v.getField();
                String defaultMessage = v.getDefaultMessage();
                stringJoiner.add(field + ": " + defaultMessage);
            });
        }
        resultBean.setMsg(stringJoiner.toString());
        return responseEntityBuilder.body(resultBean);
    }

    /*参数转换异常处理*/
    @ExceptionHandler(value = {HttpMessageNotReadableException.class})
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public HttpEntity<ResultBean<Void>> methodArgumentNotValidExceptionHandler(HttpServletRequest request, HttpMessageNotReadableException e, HandlerMethod method) {
        String localizedMessage = e.getLocalizedMessage();
        ResultBean<Void> resultBean = ResultBean.result(HttpStatus.BAD_REQUEST);
        HttpEntity<ResultBean<Void>> httpEntity = new HttpEntity(resultBean);
        if (localizedMessage.startsWith("JSON parse error")) {
            String through_reference_chain = localizedMessage.substring(localizedMessage.lastIndexOf("through reference chain"));
            String field = through_reference_chain.substring(through_reference_chain.lastIndexOf("[\"") + 2, through_reference_chain.lastIndexOf("\"]"));
            resultBean.setMsg("参数解析出错！请检测参数: " + field);
        }
        return httpEntity;
    }

    /*自定义异常处理*/
    @ExceptionHandler(value = {BusinessException.class})
    public void businessExceptionHandler(HttpServletRequest request, HttpServletResponse response, BusinessException e, HandlerMethod method) throws IOException {
        log.error("异常处理： businessException；\n【 远端请求地址：{}; 请求URI：{};" +
                        "\n 请求方法：{}； error message：{}】",
                request.getRemoteHost(), request.getRequestURI(),
                method.getMethod().getName(), e.getLocalizedMessage());
        response.getWriter().write(new ObjectMapper().writeValueAsString(ResultBean.error(e.getCode(), e.getMsg())));
    }


}
